#pragma once

#include "D3D11Client.h"
#include <memory>
#include <vector>

#include "TileTypes.h"

class CachedTexture;

//============================================================================
//				Rendering of tile meshes
//============================================================================

#define WIREFRAME_COLOR_MULT 2.0f
#define NUM_PLANET_SHADOW 4U

#define PLANETARY_SHADOW_DIST_LIMIT 2e6

bool ReadTexture( BYTE **data, char *fname, UINT &twidth, UINT &theight );
bool GetMipMapNumber( UINT w, UINT h, UINT &nmip );

class TileRenderMgr {

#pragma region cbuffers

	//==============================================
	//			Misc features
	//==============================================

	//atnosphere haze, basic atmosphere model and 
	typedef struct cb_AtmParams {
		D3DXVECTOR3 FogColor;
		float FogDensity;

		D3DXVECTOR3 SunDir;
		float Dispersion;

		float SunAppRad;
		float Ambient0;
		float GlobalAmb;
		float dist_scale;

		D3DXVECTOR3 PlanetPosW;
		float exposure;
		//64
	} cb_AtmParams;

	//eclipses:
	typedef struct cb_PlanetaryShadowParams {
		struct {
			D3DXVECTOR3 PosW;
			float rad;

			UINT hasAtm;
			float Dispersion;
			float nothing[2];
		} Planet[4];
		D3DXVECTOR3 SunPosW;
		UINT nplanet;
		//144
	} cb_PlanetaryShadowParams;

	//ring shadows params:
	typedef struct cb_RingParams {
		D3DXVECTOR3 NormRW;
		float irad;

		D3DXVECTOR3 PosPW;
		float orad;
		//32
	} cb_RingParams;

	//indexes and mults of diffuse and normal microtextures:
	typedef struct MicroTexSpec {
		UINT tidx;
		float tmult;
		UINT nidx;
		float nmult;
		//16
	} MicroTexSpec;

	//============================
	//			Terrain Shadows
	//============================
	typedef struct VSCB_Shadow_per_tile {
		D3DXMATRIX W;
		float TexOff_height[4];

		float coef_lat_0;
		float coef_lat_1;
		float coef_lng;
		UINT flags;

		float cos_minlat;
		float sin_minlat;
		float dist_lat;
		float dist_lng;
		//112
	} VSCB_Shadow_per_tile;

	//============================
	//			Tiles
	//============================
	typedef struct VSCB_per_frame {
		D3DXMATRIX VP;
		float dist_scale;
		float coef_altitude;
		float waste;
		float disp_microtex_range;
		//64
	} VSCB_per_frame;

	//
	typedef struct VSCB_per_tile {
		D3DXMATRIX W;
		float TexOff[4];
		float TexOff_norm[4];
		float TexOff_height[4];
		float TexOff_lcover[4];

		float coef_lat_0;
		float coef_lat_1;
		float coef_lng;
		UINT flags;

		float cos_minlat;
		float sin_minlat;
		float dist_lat;
		float dist_lng;
		//160
	} VSCB_per_tile;	//atmosphere parameters for all FL and DetailsManager

	typedef struct HSCB_per_tile {
		float edge_tf;
		float min_inside_tf;
		float max_inside_tf;
		float coef_diff;
		//16
	} HSCB_per_tile;

	typedef struct PSCB_per_frame {
		D3DXVECTOR3 SunDir;
		float water_spower;
		D3DXVECTOR3 Ambient;
		float coef_altitude;
		//32
	} PSCB_per_frame;

	typedef struct PSCB_per_tile {
		UINT32 flags;
		float max_microtex_dist;	//maximum distance where microtex is still used
		float color_mult;
		float waste;
		//16
	} PSCB_per_tile;

	typedef struct TILE_RES_LVL {
		UINT vdim;
		float edge_tf[2];
		float inside_tf[2];

		UINT sidx[2];
		UINT nidx[2];
		UINT svtx[2];
		UINT nvtx[2];

	} TILE_RES_LVL;

#pragma endregion

public:
	TileRenderMgr();
	~TileRenderMgr();

	static void GlobalInit( ID3D11Buffer **_CB_AtmParams, ID3D11Buffer **_CB_PlanetShadows );
	static void GlobalExit();

	void UpdatePerFrameBuffers( float dist_scale, float coef_altitude, float luminance );
	void RenderSphere( UINT8 level, UINT npatch, TILEDESC1 *td, D3DXMATRIX *mWorld, float dist_scale, float luminance );	
	void RenderLowResTiles( TILERENDERSPEC *trs, UINT ntile, float dist_scale, float luminance );
	void RenderHighResTiles( TILERENDERSPEC *trs, UINT ntile, float dist_scale, float coef_altitude, float luminance );

	void RenderTerrainShadows( std::vector<TILERENDERSPEC> &trs, float dist_scale, float coef_altitude );
	
	static void SetWaterMicroTexture( const char *fname );

	float InitResources( float **flat_disp_data, float **coarse_disp_data, D3D11Client * client );
private:
	bool AddTexture( std::vector<BYTE*> &TEX, BYTE *data, UINT *twidth, UINT *theight, UINT &nmip );

	static void CreateSphere( TILEMESH1 &mesh, UINT nrings, bool hemisphere, int which_half, int texres );
	static void CreateSpherePatch_LOW( TILEMESH1 &mesh, UINT ilat, UINT nlat, UINT nlng, UINT res, UINT bseg = 0xFFFFFFFF, bool reduce = true, bool shift_origin = false );
	static void CreateSpherePatch_HIGH( TILEVERTEX_HIGH *vtx, UINT16 *Idx, UINT ilat, UINT res, UINT bseg = 0xFFFFFFFF );

	std::vector<D3D10_SHADER_MACRO> SM;	

	bool
		bShowWireFrame;
	
	//same for any planet
	static UINT FL;		//current feature level of gd3dDevice

	static CComPtrEx<ID3D11InputLayout> IL_TILEVERTEX_HIGH;
	static ID3D11InputLayout
		*IL_TILEVERTEX_LOW;///,		//xyz positions		L1-L7
		//*IL_TILEVERTEX_HIGH;	//angular positions	L8-LMAX
	static UINT
		Stride_TILEVERTEX_LOW,		//L1-L7		with xyz coords
		Stride_TILEVERTEX_HIGH;		//L8-LMAX	with angular coords
	static ID3D11VertexShader
		*VS_Tile_Far[2];//basic atmosphere + other
	static ID3D11SamplerState
		*SS_HeightMap,
		*SS_TileTexture,
		*SS_MicroTexture;
	static ID3D11ShaderResourceView
		*WaterMicroTex;
	static ID3D11Buffer
	//tile mesh:
		*VB_TPL, *IB,		//vertex/index buffers for levels L8-L18
	//terrain:
		*CB_AtmParams,			//atmosphere and constant lighting parameters
		*CB_PlanetShadowParams,	//data required for computation of planetary shadows
		*CB_RingParams,
		*VSCB_Tile[2],
		*HSCB_Tile,
		*PSCB_Tile[2];
	static ID3D11DepthStencilState
		*DSS_Tile;
	static UINT patchidx[9];	//indexes of starting TILEDESC1 structs of specific levels in tiledesc
	static UINT NLAT[9];		//number of latitude segments for L1-L8
	static UINT NLNG5[1], NLNG6[2], NLNG7[4], NLNG8[8], *NLNG[9];
	static TILEMESH1 TPL_1,	TPL_2, TPL_3, TPL_4[2], TPL_5, TPL_6[2], TPL_7[4],
		*TPL[8];			//low-res tile meshes
	static TILE_RES_LVL TRES[3];//12/12 [I], 34/18 [II], 66/26 [III]

	//compiled for each planet (actual code depends on config settings)
	CComPtrEx<ID3D11VertexShader>
		VS_Tile;
	CComPtrEx<ID3D11HullShader>
		HS_Tile;
	CComPtrEx<ID3D11DomainShader>
		DS_Tile;
	CComPtrEx<ID3D11PixelShader>
		PS_Tile;
	ID3D11ShaderResourceView
		*DisplacementMicroTex,
		*aTerrainMicroTex;
	ID3D11Buffer
		*CB_ForestMicro;

	//shadows:
	struct CBShadow_per_frame
	{
		float dist_scale;
		float coef_altitude;
		float nothing[2];
	};

	struct CBShadow_per_tile
	{
		D3DXMATRIX mWorld;
		float TexOff_height[4];
		float coef_lat_0;
		float coef_lat_1;
		float coef_lng;
		UINT ncascade;

		float cos_minlat;
		float sin_minlat;
		float nothing[2];
	};

	static CComPtrEx<ID3D11VertexShader>
		VS_Shadow;
	static CComPtrEx<ID3D11GeometryShader>
		GS_Shadow;
	static CComPtrEx<ID3D11PixelShader>
		PS_Shadow;
	static CComPtrEx<ID3D11Buffer>
		cb_per_frame,
		cb_per_tile;

	static void InitTerrainShadows();
	static void ExitTerrainShadows();

public:
	const D3D10_SHADER_MACRO* GetShaderConfig() { return SM.size() > 0 ? &SM[0] : nullptr; }
};
